package chen.web.stat;


import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;

import chen.web.DataSourceProvider;

public class Statistics{
	private static int id = 0;
	private static final int RequestInfoCacheSize = 10;
	private static Map<Integer, RequestInfo> RequestInfoCache = null;
	private static Map<Integer, RequestInfo> OldRequestInfoCache = null;
	private static SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(DataSourceProvider.getDataSource());
	
	private static final String SQL_0 = "select stat_id from web_stat_info order by stat_id desc limit 1";
	static{
		try{
			id = simpleJdbcTemplate.queryForInt(SQL_0);
		} catch(EmptyResultDataAccessException exception){
			//do nothing
		}
		initRequestInfoCache();
	}
	
	private static void initRequestInfoCache(){
		RequestInfoCache = new LinkedHashMap<Integer, RequestInfo>(RequestInfoCacheSize+5);
	}
	
	public static int save(final RequestInfo requestInfo){
		RequestInfoCache.put(requestInfo.getId(), requestInfo);
		//清空缓存，持久化
		if(RequestInfoCache.size()>RequestInfoCacheSize){
			synchronized(Statistics.class){
				if(RequestInfoCache.size()>RequestInfoCacheSize){
					OldRequestInfoCache = RequestInfoCache;
					initRequestInfoCache();
					Thread thread = new Thread(new Runnable(){
						@Override
						public void run() {
							// TODO Auto-generated method stub
							saveRequestInfoCache(OldRequestInfoCache);
						}
					});
					thread.start();
				}
			}
		}
		return id;
	}

	private static final String SQL_1 = "insert into web_stat_info(stat_id,stat_ip,stat_uuid,acc_id,acc_nickname,stat_host,stat_requestURL,stat_requestMethod,stat_referer,stat_userAgent,stat_accept,stat_acceptLanguage,stat_acceptEncoding,stat_acceptCharset,stat_keepAlive,stat_requestTime,stat_clientWidth,stat_clientHeight,stat_screenWidth,stat_screenHeight,stat_timeZone,stat_color,stat_responseStatus, stat_processTime,stat_remainedTime) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
	private static void saveRequestInfoCache(Map<Integer, RequestInfo> cache){
		List<Object[]> batchArgs = new ArrayList<Object[]>(cache.size() + 5);
		Iterator<Map.Entry<Integer, RequestInfo>> iterator = cache.entrySet().iterator();
		while(iterator.hasNext()){
			Map.Entry<Integer, RequestInfo> entry = iterator.next();
			RequestInfo info = cache.get(entry.getKey());
			iterator.remove();
			if(info.getClientInfo() == null){
				info.setClientInfo(new ClientInfo());
			}
			Object[] args = new Object[]{
				info.getId(), info.getIp(), info.getUuid(), info.getId(), info.getNickname(),
				info.getHost(), info.getRequestURL(), info.getRequestMethod(), info.getReferer(), info.getUserAgent(), info.getAccept(),
				info.getAcceptLanguage(), info.getAcceptEncoding(), info.getAcceptCharset(), info.getKeepAlive(), new Timestamp(info.getRequestTime().getTime()),
				info.getClientInfo().getClientWidth(),info.getClientInfo().getClientHeight(),info.getClientInfo().getScreenWidth(),info.getClientInfo().getScreenHeight(),info.getClientInfo().getClienTimeZone(),
				info.getClientInfo().getScreenColor(),info.getResponseStatus(),info.getProcessTime(),info.getRemainedTime()
			};
			batchArgs.add(args);
		}
		simpleJdbcTemplate.batchUpdate(SQL_1, batchArgs);
	}
	
	private static final String SQL_3 = "update web_stat_info set stat_clientWidth=?,stat_clientHeight=?,stat_screenWidth=?,stat_screenHeight=?,stat_timeZone=?,stat_color=?,stat_remainedTime = ?-UNIX_TIMESTAMP(stat_requestTime) where stat_id=?";
	public static void update(ClientInfo info){
		RequestInfo cacheInfo = RequestInfoCache.get(info.getStatId());
		if(cacheInfo == null){
			RequestInfo oldCacheInfo = OldRequestInfoCache == null ?null : OldRequestInfoCache.get(info.getStatId());
			if(oldCacheInfo != null){
				oldCacheInfo.setClientInfo(info);
				int remainedTime = (int)(System.currentTimeMillis()/1000 - oldCacheInfo.getRequestTime().getTime()/1000);
				oldCacheInfo.setRemainedTime(remainedTime);			
			}
			simpleJdbcTemplate.update(SQL_3, info.getClientWidth(),info.getClientHeight(),info.getScreenWidth(),info.getScreenHeight(),info.getClienTimeZone(), info.getScreenColor(), System.currentTimeMillis()/1000, info.getStatId());
		} else {
			cacheInfo.setClientInfo(info);
			int remainedTime = (int)(System.currentTimeMillis()/1000 - cacheInfo.getRequestTime().getTime()/1000);
			cacheInfo.setRemainedTime(remainedTime);
		}
	}
	
	private static final String SQL_5 = "select * from web_stat_info where stat_id = ?";
	public static RequestInfo get(int id){
		RequestInfo info = null;
		try{
			info = simpleJdbcTemplate.queryForObject(SQL_5, new RequestInfoRowMapper(), id);	
		} catch(EmptyResultDataAccessException exception){
			//do nothing
		}
		return info;
	}
	
	private static final String SQL_6 = "select *, count(*) as stat_count from web_stat_info where stat_requestTime between ?,? group by stat_requestURL order by stat_count desc limit ?,?";
	public static List<RequestInfoCount> entryUrlList(Date begin, Date end, int startOffset, int count){
		return simpleJdbcTemplate.query(SQL_6, new RequestInfoCountRowMapper(), begin, end, startOffset, count);
	}
	
	private static class RequestInfoCountRowMapper implements RowMapper<RequestInfoCount>{

		@Override
		public RequestInfoCount mapRow(ResultSet rs, int index)
				throws SQLException {
			// TODO Auto-generated method stub
			RequestInfoRowMapper mapper = new RequestInfoRowMapper();
			RequestInfo info = mapper.mapRow(rs, index);
			int count = rs.getInt("stat_count");
			RequestInfoCount requestInfoCount = new RequestInfoCount(info, count);
			return requestInfoCount;
		}
		
	}
	
	private static class RequestInfoRowMapper implements RowMapper<RequestInfo>{

		@Override
		public RequestInfo mapRow(ResultSet rs, int arg1) throws SQLException {
			// TODO Auto-generated method stub
			RequestInfo info = new RequestInfo();
			info.setId(rs.getInt("stat_id"));
			info.setAccept(rs.getString("stat_accept"));
			info.setAcceptCharset(rs.getString("stat_acceptCharset"));
			info.setAcceptEncoding(rs.getString("stat_acceptEncoding"));
			info.setAcceptLanguage(rs.getString("stat_acceptLanguage"));
			info.setAccountId(rs.getInt("acc_id"));
			info.setConnection(rs.getString("stat_connection"));
			info.setHost(rs.getString("stat_host"));
			info.setIp(rs.getString("stat_ip"));
			info.setKeepAlive(rs.getString("stat_keepAlive"));
			info.setRemainedTime(rs.getInt("stat_remainedTime"));
			info.setNickname(rs.getString("acc_nickname"));
			info.setReferer(rs.getString("acc_referer"));
			info.setRequestMethod(rs.getString("stat_requestMethod"));
			info.setRequestTime(rs.getTimestamp("stat_requestTime"));
			info.setRequestURL(rs.getString("stat_requestURL"));
			info.setUserAgent(rs.getString("stat_userAgent"));
			info.setUuid(rs.getString("stat_userAgent"));
			info.setResponseStatus(rs.getInt("stat_responseStatus"));
			ClientInfo clientInfo = new ClientInfo();
			clientInfo.setClientHeight(rs.getInt("stat_clientHeight"));
			clientInfo.setClienTimeZone(rs.getInt("stat_clienTimeZone"));
			clientInfo.setClientWidth(rs.getInt("stat_clientWidth"));
			clientInfo.setScreenColor(rs.getInt("stat_screenColor"));
			clientInfo.setScreenHeight(rs.getInt("stat_screenHeight"));
			clientInfo.setScreenWidth(rs.getInt("stat_screenWidth"));
			info.setClientInfo(clientInfo);
			return info;
		}
		
	}
	
	static synchronized int generalStatId(){
		return ++id;
	}
	
	public static void flush(){
		OldRequestInfoCache = RequestInfoCache;
		initRequestInfoCache();
		saveRequestInfoCache(OldRequestInfoCache);
	}
	
}
